#pragma once

#include <linux/compiler.h>
#include <linux/types/page.h>
#include <linux/mmzone.h>
#include <linux/minmax.h>
#include <linux/list.h>
#include <linux/stddef.h>

typedef struct zone MemZone_t;
typedef struct page Page_t;
typedef struct free_area FreeArea_t;

static inline void __add_to_free_list(struct page *page, struct zone *zone,
                                      unsigned int order, int migratetype,
                                      bool tail)
{
    struct free_area *area = &zone->free_area[order];

    if (tail)
        list_add_tail(&page->buddy_list, &area->free_list[migratetype]);
    else
        list_add(&page->buddy_list, &area->free_list[migratetype]);
    area->nr_free++;
}

static inline void __del_page_from_free_list(struct page *page, struct zone *zone,
                                             unsigned int order, int migratetype)
{
    list_del_init(&page->buddy_list);
    zone->free_area[order].nr_free--;
}

static inline FreeArea_t* __zone_freearea(MemZone_t *zone, uint32_t order)
{
    return &zone->free_area[order];
}

static inline bool __area_is_empty(FreeArea_t *area)
{
    return list_empty(&area->free_list[0]);
}

static inline Page_t* __area_first_page(FreeArea_t *area)
{
    return list_first_entry(&area->free_list[0], Page_t, buddy_list);
}

#define __for_each_page_in_area(area, page) \
    list_for_each_entry(page, &area->free_list[0], buddy_list)

static inline Page_t* __zone_page_at(MemZone_t *zone, uintptr_t index)
{
    Page_t* page;

    if (index < zone->spanned_pages)
    {
        page = &zone->zone_mem_map[index];
    }
    else
    {
        page = NULL;
    }

    return page;
}

#define __page_index(zone, page) ((page)->index)

#define __zone_spanned_pages(zone) ((zone)->spanned_pages)

static inline void __page_init_one(Page_t *page, unsigned long index)
{
    page->index = index;
    page->order = MAX_PAGE_ORDER;
    page->_refcount.counter = 0;
    INIT_LIST_HEAD(&page->buddy_list);
}

Page_t *OsPagesAllocContiguous(MemZone_t *zone, size_t nPages);
void OsPagesFreeContiguous(MemZone_t *zone, Page_t *page, size_t nPages);

#define __zone_add_pages(zone, page, n) OsPagesFreeContiguous(zone, page, n)
